package com.gitee.wsl.collections

import com.gitee.wsl.collections.bag.HashBag
import com.gitee.wsl.collections.bag.PredicatedBag
import com.gitee.wsl.collections.bag.api.Bag
import com.gitee.wsl.collections.delegate.DelegatingMutableCollection
import com.gitee.wsl.func.predicate.Predicate
import kotlin.jvm.JvmOverloads


/**
 * Decorates another [Collection] to validate that additions
 * match a specified predicate.
 *
 *
 * This collection exists to provide validation for the decorated collection.
 * It is normally created to decorate an empty collection.
 * If an object cannot be added to the collection, an IllegalArgumentException is thrown.
 *
 *
 *
 * One usage would be to ensure that no null entries are added to the collection:
 *
 * <pre>
 * Collection coll = PredicatedCollection.predicatedCollection(new ArrayList(), NotNullPredicate.INSTANCE);
</pre> *
 *
 *
 * This class is Serializable from Commons Collections 3.1.
 *
 *
 * @param <E> the type of the elements in the collection
 * @since 3.0
</E> */
open class PredicatedCollection<E> protected constructor(
    collection: MutableCollection<E>,
    /** The predicate to use  */
    protected val predicate: Predicate<E>
) : DelegatingMutableCollection<E>(collection) {
    /**
     * Builder for creating predicated collections.
     *
     *
     * Create a Builder with a predicate to validate elements against, then add any elements
     * to the builder. Elements that fail the predicate will be added to a rejected list.
     * Finally, create or decorate a collection using the createPredicated[List,Set,Bag,Queue] methods.
     *
     *
     * An example:
     * <pre>
     * Predicate&lt;String&gt; predicate = NotNullPredicate.notNullPredicate();
     * PredicatedCollectionBuilder&lt;String&gt; builder = PredicatedCollection.builder(predicate);
     * builder.add("item1");
     * builder.add(null);
     * builder.add("item2");
     * List&lt;String&gt; predicatedList = builder.createPredicatedList();
    </pre> *
     *
     *
     * At the end of the code fragment above predicatedList is protected by the predicate supplied
     * to the builder, and it contains item1 and item2.
     *
     *
     * More elements can be added to the builder once a predicated collection has been created,
     * but these elements will not be reflected in already created collections.
     *
     * @param <E>  the element type
     * @since 4.1
    </E> */
    class Builder<E>(private val predicate: Predicate<E>) {
        /** The predicate to use.  */
        //private val predicate: Predicate<in E>

        /** The buffer containing valid elements.  */
        private val accepted = ArrayList<E>()

        /** The buffer containing rejected elements.  */
        private val rejected = ArrayList<E>()


        /**
         * Adds the item to the builder.
         *
         *
         * If the predicate is true, it is added to the list of accepted elements,
         * otherwise it is added to the rejected list.
         *
         * @param item  the element to add
         * @return the PredicatedCollectionBuilder.
         */
        fun add(item: E): Builder<E> {
            if (predicate.test(item)) {
                accepted.add(item)
            } else {
                rejected.add(item)
            }
            return this
        }

        /**
         * Adds all elements from the given collection to the builder.
         *
         *
         * All elements for which the predicate evaluates to true will be added to the
         * list of accepted elements, otherwise they are added to the rejected list.
         *
         * @param items  the elements to add to the builder
         * @return the PredicatedCollectionBuilder.
         */
        fun addAll(items: Collection< E>): Builder<E> {
            for (item in items) {
                add(item)
            }
            return this
        }

        /**
         * Decorates the given bag with validating behavior using the predicate. All accepted elements
         * are appended to the bag. If the bag already contains elements, they are validated.
         *
         *
         * The builder is not modified by this method, so it is possible to create more collections
         * or add more elements afterwards. Further changes will not propagate to the returned bag.
         *
         * @param bag  the bag to decorate, must not be null
         * @return the decorated bag.
         * @throws NullPointerException if bag is null
         * @throws IllegalArgumentException if bag contains invalid elements
         */
        @JvmOverloads
        fun createPredicatedBag(bag: Bag<E> = HashBag()): Bag<E> {
            val predicatedBag = PredicatedBag.predicatedBag(bag, predicate)
            predicatedBag.addAll(accepted)
            return predicatedBag
        }

        /**
         * Create a new predicated list filled with the accepted elements.
         *
         *
         * The builder is not modified by this method, so it is possible to create more collections
         * or add more elements afterwards. Further changes will not propagate to the returned list.
         *
         * @return a new predicated list.
         */
//        fun createPredicatedList(): List<E> {
//            return createPredicatedList(ArrayList())
//        }

        /**
         * Decorates the given list with validating behavior using the predicate. All accepted elements
         * are appended to the list. If the list already contains elements, they are validated.
         *
         *
         * The builder is not modified by this method, so it is possible to create more collections
         * or add more elements afterwards. Further changes will not propagate to the returned list.
         *
         * @param list  the List to decorate, must not be null
         * @return the decorated list.
         * @throws NullPointerException if list is null
         * @throws IllegalArgumentException if list contains invalid elements
         */
//        fun createPredicatedList(list: List<E>): List<E> {
//            val predicatedList = PredicatedList.predicatedList(list, predicate)
//            predicatedList.addAll(accepted)
//            return predicatedList
//        }

        /**
         * Decorates the given multiset with validating behavior using the predicate. All accepted elements
         * are appended to the multiset. If the multiset already contains elements, they are validated.
         *
         *
         * The builder is not modified by this method, so it is possible to create more collections
         * or add more elements afterwards. Further changes will not propagate to the returned multiset.
         *
         * @param multiset  the multiset to decorate, must not be null
         * @return the decorated multiset.
         * @throws NullPointerException if multiset is null
         * @throws IllegalArgumentException if multiset contains invalid elements
         */
        /**
         * Create a new predicated multiset filled with the accepted elements.
         *
         *
         * The builder is not modified by this method, so it is possible to create more collections
         * or add more elements afterwards. Further changes will not propagate to the returned multiset.
         *
         * @return a new predicated multiset.
         */
//        @JvmOverloads
//        fun createPredicatedMultiSet(multiset: MultiSet<E> = HashMultiSet<E>()): MultiSet<E> {
//            val predicatedMultiSet = PredicatedMultiSet.predicatedMultiSet(multiset, predicate)
//            predicatedMultiSet.addAll(accepted)
//            return predicatedMultiSet
//        }

        /**
         * Decorates the given queue with validating behavior using the predicate. All accepted elements
         * are appended to the queue. If the queue already contains elements, they are validated.
         *
         *
         * The builder is not modified by this method, so it is possible to create more collections
         * or add more elements afterwards. Further changes will not propagate to the returned queue.
         *
         * @param queue  the queue to decorate, must not be null
         * @return the decorated queue.
         * @throws NullPointerException if queue is null
         * @throws IllegalArgumentException if queue contains invalid elements
         */
//        @JvmOverloads
//        fun createPredicatedQueue(queue: Queue<E> = LinkedList<Any>()): Queue<E> {
//            Objects.requireNonNull(queue, "queue")
//            val predicatedQueue: PredicatedQueue<E?> = PredicatedQueue.predicatedQueue(queue, predicate)
//            predicatedQueue.addAll(accepted)
//            return predicatedQueue
//        }

        /**
         * Create a new predicated set filled with the accepted elements.
         *
         *
         * The builder is not modified by this method, so it is possible to create more collections
         * or add more elements afterwards. Further changes will not propagate to the returned set.
         *
         * @return a new predicated set.
         */
//        fun createPredicatedSet(): Set<E> {
//            return createPredicatedSet(HashSet())
//        }

        /**
         * Decorates the given list with validating behavior using the predicate. All accepted elements
         * are appended to the set. If the set already contains elements, they are validated.
         *
         *
         * The builder is not modified by this method, so it is possible to create more collections
         * or add more elements afterwards. Further changes will not propagate to the returned set.
         *
         * @param set  the set to decorate, must not be null
         * @return the decorated set.
         * @throws NullPointerException if set is null
         * @throws IllegalArgumentException if set contains invalid elements
         */
//        fun createPredicatedSet(set: Set<E>): Set<E> {
//            val predicatedSet = PredicatedSet.predicatedSet(set, predicate)
//            predicatedSet.addAll(accepted)
//            return predicatedSet
//        }

        /**
         * Returns an unmodifiable collection containing all rejected elements.
         *
         * @return an unmodifiable collection
         */
        fun rejectedElements(): Collection<E> {
            return rejected
        }
    }

    /**
     * Constructor that wraps (not copies).
     *
     *
     * If there are any elements already in the collection being decorated, they
     * are validated.
     *
     * @param collection  the collection to decorate, must not be null
     * @param predicate  the predicate to use for validation, must not be null
     * @throws NullPointerException if collection or predicate is null
     * @throws IllegalArgumentException if the collection contains invalid elements
     */
    init {
        for (item in collection) {
            validate(item)
        }
    }

    /**
     * Override to validate the object being added to ensure it matches
     * the predicate.
     *
     * @param element  the object being added
     * @return the result of adding to the underlying collection
     * @throws IllegalArgumentException if the add is invalid
     */
     override fun add(element: E): Boolean {
        validate(element)
        return delegate.add(element)
    }

    /**
     * Override to validate the objects being added to ensure they match
     * the predicate. If anyone fails, no update is made to the underlying
     * collection.
     *
     * @param elements  the collection being added
     * @return the result of adding to the underlying collection
     * @throws IllegalArgumentException if the add is invalid
     */
     override fun addAll(elements: Collection<E>): Boolean {
        for (item in elements) {
            validate(item)
        }
        return delegate.addAll(elements)
    }

    /**
     * Validates the object being added to ensure it matches the predicate.
     *
     *
     * The predicate itself should not throw an exception, but return false to
     * indicate that the object cannot be added.
     *
     * @param object  the object being added
     * @throws IllegalArgumentException if the add is invalid
     */
    protected fun validate(`object`: E) {
        require(predicate.test(`object`)) { "Cannot add Object '$`object`' - Predicate '$predicate' rejected it" }
    }

    companion object {

        /**
         * Returns a Builder with the given predicate.
         *
         * @param <E>  the element type
         * @param predicate  the predicate to use
         * @return a new Builder for predicated collections
         * @since 4.1
        </E> */
        fun <E> builder(predicate: Predicate<E>): Builder<E> {
            return Builder(predicate)
        }

        /**
         * Returns a Builder with a NotNullPredicate.
         *
         * @param <E>  the element type
         * @return a new Builder for predicated collections that ignores null values.
         * @since 4.1
        </E> */
//        fun <E> notNullBuilder(): Builder<E> {
//            return PredicatedCollection.Builder<E>(NotNullPredicate.< E > notNullPredicate < E ? > ())
//        }

        /**
         * Factory method to create a predicated (validating) collection.
         *
         *
         * If there are any elements already in the collection being decorated, they
         * are validated.
         *
         * @param <T> the type of the elements in the collection
         * @param coll  the collection to decorate, must not be null
         * @param predicate  the predicate to use for validation, must not be null
         * @return a new predicated collection
         * @throws NullPointerException if collection or predicate is null
         * @throws IllegalArgumentException if the collection contains invalid elements
         * @since 4.0
        </T> */
        fun <T> predicatedCollection(
            coll: MutableCollection<T>,
            predicate: Predicate<T>,
        ): PredicatedCollection<T> {
            return PredicatedCollection(coll, predicate)
        }
    }
}